/**
 * OSS is a Open Service Container which gives the leverage of building the Desktop and Web
 * Application. * Copyright (C) 2008-2009  Ponraj Sutanthiramani
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Or5e Open Source Community
 * 105, S.S Paradise Apartment, Munnireddy Layout, Horamavu, Banaswadi, Bangalore, India.
 */
package org.or5e.api.mp;

import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.or5e.api.mp.player.audio.AudioPlayer;
import org.or5e.api.mp.player.audio.PlaybackEvent;
import org.or5e.api.mp.player.audio.PlayerException;
import org.or5e.api.mp.playlist.Playlist;
import org.or5e.api.mp.playlist.Playlist.PlayMode;
import org.or5e.api.mp.playlist.PlaylistException;
import org.or5e.api.mp.playlist.PlaylistIO;
import org.or5e.api.mp.playlist.PlaylistItem;
import org.or5e.core.BaseException;
import org.or5e.core.BaseObject;

/**
 * This is the Default Media Player SPI that will be used by the core container.
 * 
 * @version 1.0b
 * @since 0.1b
 * @author Ponraj Suthanthiramani
 */
public class MediaPlayerSPI extends BaseObject implements MediaPlayer {

	/**
	 * Media Player Feature:
	 * <ol>
	 * <li>Play MP3 files</li>
	 * <li>Go to next song if the current song ends</li>
	 * <li>Keep track of no of times played for a compete listened songs</li>
	 * <li>Maintain 4 list of Play List</li>
	 * <li>Provide controls for API for the following operations
	 * <ul>
	 * <li>Play, Prev, Play, Pause, Resume, Stop, Next</li>
	 * <li>Seek</li>
	 * <li>Volume, Mute and UnMute</li>
	 * <li>Equalizer presets</li>
	 * </ul>
	 * </li>
	 * <li>Shutdown MPlayer gracefully in abnormal conditions</li>
	 * </ol>
	 */

	private AudioPlayer _player = null;
	private Boolean isMPInitilized = Boolean.FALSE;
	private Boolean isPlayingAudio = Boolean.FALSE;
	private Playlist playList = null;
	private MediaPlaybackListener mediaListener = null;

	private List<Playlist> availablePlayList = new ArrayList<Playlist>();
	private List<MediaEventListener> mediaListeners = new ArrayList<MediaEventListener>();

	@Override
	public String getName() {
		return "MediaPlayerSPI";
	}

	/**
	 * This method will initilize the Media player SPI so that it holds good for
	 * all the operation.
	 * 
	 * @since 0.1b
	 * @throws MediaException
	 */
	@Override
	public void init(String configFile) throws MediaException {
		info("Initilizing all the Media Player Settings.");
		Settings.loadSettings();

		info("Initilizing Play list.");
		String mixerName = Settings.getMixerName();
		File defaultPlayList = new File("data/playlist/myplaylist.xspf");
		if (defaultPlayList.isFile()) {
			List<PlaylistItem> pliList;
			try {
				pliList = PlaylistIO.load(defaultPlayList.getAbsolutePath());
				playList = new Playlist();
				for (PlaylistItem playlistItem : pliList) {
					playList.addItem(playlistItem);
				}

				_player = new AudioPlayer();
				mediaListener = new MediaPlaybackListener(playList, _player) {
					@Override
					public String getName() {
						return "MediaPlaybackListener";
					}

					@Override
					public void playbackBuffering(PlaybackEvent pe) {
					}

					@Override
					public void playbackOpened(PlaybackEvent pe) {
					}

					@Override
					public void playbackPlaying(PlaybackEvent pe) {
					}

					@Override
					public void playbackProgress(PlaybackEvent pe) {
					}

					@Override
					public void playbackPaused(PlaybackEvent pe) {
					}

					@Override
					public void playbackStopped(PlaybackEvent pe) {
					}
				};
				_player.addPlaybackListener(mediaListener);

				if (mixerName != null) {
					_player.setMixerName(mixerName);
				}

				isMPInitilized = Boolean.TRUE;
			} catch (PlaylistException e) {
				isMPInitilized = Boolean.FALSE;
				new MediaException(MediaExceptionConstants.PLAYERINITERROR);
			}
		}
	}

	/**
	 * This Implementation needs the implicit destruction. so API Customer
	 * should call this to get this destructed. Again a Self destructive
	 * processor is encorporated with this Implementaion.
	 * 
	 * @since 0.1b
	 * @throws MediaException
	 */
	@Override
	public void destroy() throws MediaException {
		// Requirement: Dont proceed if the Media Player is not initilzied.
		if (!isMPInitilized)
			throw new MediaException(MediaExceptionConstants.NOTINITILIZED);
		info("Media Player Component is Deinitilizing...");
		_player.stop();
		info("Player is stopped and the Audio strems are closed.");
	}

	/**
	 * Play a MP3 track if the API Customer calls it.
	 * 
	 * @since 0.1b
	 * @throws MediaException
	 */
	@Override
	public void play() throws MediaException {
		// Requirement: Dont proceed if the Media Player is not initilzied.
		if (!isMPInitilized)
			throw new MediaException(MediaExceptionConstants.NOTINITILIZED);
		try {
			// If the player is busy playing current track first stop that
			if (isPlayingAudio) {
				_player.stop();
			}

			// get the current playlist cursor
			// Requirement: If the Cursor is BOF then go to next and start
			// running the first song.
			PlaylistItem pli = playList.getCursor();
			if (pli == null) {
				playList.nextCursor();
				pli = playList.getCursor();
			}

			if (pli != null) {
				// Open the location and start playing the songs.
				_player.open(new File(pli.getLocation()));

				// Volume to be set approriate before it starts playing and
				// after opening the player.
				setVolume(Settings.getGain() / 100.0F);
				_player.play();
				isPlayingAudio = Boolean.TRUE;
			}
		} catch (PlayerException ex) {
			isPlayingAudio = Boolean.FALSE;
			throw new MediaException(ex.getMessage());
		}
	}

	/**
	 * Play a MP3 track if the API Customer calls it.
	 * 
	 * @since 0.1b
	 * @throws MediaException
	 */
	@Override
	public void play(String playFile) throws MediaException {
		if (_player.getState() == AudioPlayer.PLAY
				|| _player.getState() == AudioPlayer.PAUSE) {
			_player.stop();
		}
		File file = new File(playFile);
		if (file.exists()) {
			try {
				_player.open(file);
				_player.play();
			} catch (PlayerException e) {
				e.printStackTrace();
			}
			this.playList = null;
		}
	}

	/**
	 * Play a MP3 track if the API Customer calls it.
	 * 
	 * @since 0.1b
	 * @throws MediaException
	 */
	@Override
	public void play(URL playURL) throws MediaException {
		try {
			if (_player.getState() == AudioPlayer.PLAY
					|| _player.getState() == AudioPlayer.PAUSE) {
				_player.stop();
			}
			_player.open(playURL);
			_player.play();
		} catch (PlayerException e) {
			e.printStackTrace();
		}
		this.playList = null;
	}

	/**
	 * This method will pause the current track and returns the position where
	 * it is been paused so that it can be passed when they call resume.
	 * 
	 * @return of type @see java.lang.Long
	 * @since 0.1b
	 * @throws MediaException
	 */
	@Override
	public Long pause() throws MediaException {
		// Requirement: Dont proceed if the Media Player is not initilzied or
		// the Audio is not Playing
		if (!isMPInitilized)
			throw new MediaException(MediaExceptionConstants.NOTINITILIZED);
		if (!isPlayingAudio)
			throw new MediaException(MediaExceptionConstants.NOMEDIAPLAYING);
		_player.pause();
		return _player.getPosition();
	}

	/**
	 * This method will start playing from the position passed as the parameter.
	 * 
	 * @param startFrom
	 *            of type @see java.lang.Long
	 * @since 0.1b
	 * @throws MediaException
	 */
	@Override
	public synchronized void resume(Long startFrom) throws MediaException {
		// Requirement: Dont proceed if the Media Player is not initilzied or
		// the Audio is not Playing
		if (!isMPInitilized)
			throw new MediaException(MediaExceptionConstants.NOTINITILIZED);
		if (!isPlayingAudio)
			throw new MediaException(MediaExceptionConstants.NOMEDIAPLAYING);
		try {
			// Seek to the requested position so that it starts playing from that
			// position
			_player.seek(startFrom);
			_player.play();
		} catch (PlayerException ex) {
			throw new MediaException(ex.getMessage());
		}
	}

	/**
	 * This method will set the position of the current playing track to the
	 * requested.
	 * 
	 * @param duration
	 *            of type @see java.lang.Float
	 * @since 0.1b
	 * @throws MediaException
	 */
	@Override
	public synchronized void seek(long duration) throws MediaException {
		// Requirement: Dont proceed if the Media Player is not initilzied.
		if (!isMPInitilized)
			throw new MediaException(MediaExceptionConstants.NOTINITILIZED);
		try {
			_player.seek(duration);
		} catch (PlayerException ex) {
			throw new MediaException(ex.getMessage());
		}
	}

	/**
	 * This method will stop the current playing track.
	 * 
	 * @since 0.1b
	 * @throws MediaException
	 */
	@Override
	public void stop() throws MediaException {
		// Requirement: Dont proceed if the Media Player is not initilzied or
		// the Audio is not Playing
		if (!isMPInitilized)
			throw new MediaException(MediaExceptionConstants.NOTINITILIZED);
		if (!isPlayingAudio)
			throw new MediaException(MediaExceptionConstants.NOMEDIAPLAYING);
		_player.stop();
		isPlayingAudio = Boolean.FALSE;
	}

	/**
	 * Plays the Next MP3 Track if the API Customer callse it. If the current
	 * playing track is the last one then it moves to the first track in the
	 * playing track.
	 * 
	 * @since 0.1b
	 * @throws MediaException
	 */
	@Override
	public void next() throws MediaException {
		if (!isMPInitilized)
			throw new MediaException(MediaExceptionConstants.NOTINITILIZED);
		playList.nextCursor();
		play();
	}

	/**
	 * Plays the Previous MP3 Track if the API Customer calls it.If the current
	 * playing track is the First one then it moves to the last track in the
	 * playing track.
	 * 
	 * @since 0.1b
	 * @throws MediaException
	 */
	@Override
	public void prev() throws MediaException {
		if (!isMPInitilized)
			throw new MediaException(MediaExceptionConstants.NOTINITILIZED);
		playList.previousCursor();
		play();
	}

	/**
	 * This will randamized the current playlist so that it has a different
	 * execution order of playing list.
	 * 
	 * @since 0.1b
	 * @throws MediaException
	 */
	@Override
	public void shuffle() throws MediaException {
		// Requirement: Dont proceed if the Media Player is not initilzied.
		if (!isMPInitilized)
			throw new MediaException(MediaExceptionConstants.NOTINITILIZED);
		this.playList.randomize();
	}

	@Override
	public void repeatTrack(Boolean flag) throws MediaException {
		if (flag) {
			this.playList.setPlayMode(PlayMode.REPEAT_SINGLE);
		} else {
			this.playList.setPlayMode(PlayMode.REPEAT_NONE);
		}
	}

	@Override
	public void repeatPlaylist(Boolean flag) throws MediaException {
		if (flag) {
			this.playList.setPlayMode(PlayMode.REPEAT_ALL);
		} else {
			this.playList.setPlayMode(PlayMode.REPEAT_NONE);
		}
	}

	@Override
	public Boolean isTrackRepeat() throws MediaException {
		return this.playList.getPlayMode() == PlayMode.REPEAT_SINGLE;
	}

	@Override
	public Boolean isPlaylistRepeat() throws MediaException {
		return this.playList.getPlayMode() == PlayMode.REPEAT_ALL;
	}

	/**
	 * This method will set the gain level to the current player.
	 * 
	 * @param gain
	 *            of type @see java.lang.Float
	 * @since 0.1b
	 * @throws MediaException
	 */
	@Override
	public void setVolume(Float gain) throws MediaException {
		// Requirement: Dont proceed if the Media Player is not initilzied.
		if (!isMPInitilized)
			throw new MediaException(MediaExceptionConstants.NOTINITILIZED);
		try {
			_player.setGain(gain);
		} catch (PlayerException ex) {
			throw new MediaException(ex.getMessage());
		}
	}

	/**
	 * This method will return the current players gain level in other word
	 * Current Players playing volume.
	 * 
	 * @return of type@see java.lang.Float
	 * @since 0.1b
	 * @throws MediaException
	 */
	@Override
	public Float getVolume() throws MediaException {
		// Requirement: Dont proceed if the Media Player is not initilzied.
		if (!isMPInitilized)
			throw new MediaException(MediaExceptionConstants.NOTINITILIZED);
		return _player.getGain();
	}

	/**
	 * This metod will set the current player to mute or unmute according to the
	 * paremeter passed here.
	 * 
	 * @param mute
	 *            of type @see java.lang.Boolean
	 * @since 0.1b
	 * @throws MediaException
	 */
	@Override
	public void mute(Boolean flag) throws MediaException {
		// Requirement: Dont proceed if the Media Player is not initilzied.
		if (!isMPInitilized)
			throw new MediaException(MediaExceptionConstants.NOTINITILIZED);
		try {
			_player.setMuted(flag);
		} catch (PlayerException ex) {
			throw new MediaException(ex.getMessage());
		}
	}

	/**
	 * This method will return the true if the current player's gain is not 0.
	 * 
	 * @return of type @see java.lang.Boolean
	 * @since 0.1b
	 * @throws MediaException
	 */
	@Override
	public Boolean isMuted() throws MediaException {
		// Requirement: Dont proceed if the Media Player is not initilzied.
		if (!isMPInitilized)
			throw new MediaException(MediaExceptionConstants.NOTINITILIZED);
		return _player.isMuted();
	}

	@Override
	public List<Playlist> getAvailabilePlaylist() throws MediaException {
		return this.availablePlayList;
	}

	/**
	 * Currently this feature is not supported in this version.
	 * 
	 * @return
	 * @throws MediaException
	 */
	@Override
	public Playlist getCurrentPlayList() throws MediaException {
		// Requirement: Dont proceed if the Media Player is not initilzied.
		if (!isMPInitilized)
			throw new MediaException(MediaExceptionConstants.NOTINITILIZED);
		return this.playList;
	}

	/**
	 * Set the API callers Play list.
	 * 
	 * @since 0.1b
	 * @param playList
	 *            of type @see org.or5e.playlist.Playlist
	 * @throws MediaException
	 */
	@Override
	public void setCurrentPlaylist(Playlist playList) throws MediaException {
		// Requirement: Dont proceed if the Media Player is not initilzied.
		if (!isMPInitilized)
			throw new MediaException(MediaExceptionConstants.NOTINITILIZED);

		// Stop the current playing track if it is running.
		if (isPlayingAudio)
			_player.stop();

		// Set the current play list to the provided one.
		this.playList = playList;

		// See if the play list is the BOF then move the cursor to the first
		// track abd then play.
		if (this.playList.getCursorPosition() == -1) {
			this.playList.nextCursor();
		}
		play();
	}

	@Override
	public List<PlaylistItem> getPlayListItems(Playlist playList)
			throws MediaException {
		return playList.listItems();
	}

	@Override
	public void addPlayListItem(Playlist playList, PlaylistItem item)
			throws MediaException {
		playList.addItem(item);
	}

	@Override
	public void addPlayListItems(Playlist playList, List<PlaylistItem> items)
			throws MediaException {
		playList.addAll(items);
	}

	@Override
	public void removePlayListItem(Playlist playList, PlaylistItem item)
			throws MediaException {
		playList.removeItem(item);
	}

	@Override
	public void removePlayListItems(Playlist playList, List<PlaylistItem> items)
			throws MediaException {
		playList.removeAll(items);
	}

	@Override
	public void makePlayListLookingUpSongFolder(File songsRootFolder,
			long delayReferesh) throws MediaException {

	}

	@Override
	public void addEventListener(MediaEventListener eListener)
			throws MediaException {
		mediaListeners.add(eListener);
	}

	public static void main(String[] args) throws MediaException,
			InterruptedException {
		MediaPlayer p = new MediaPlayerSPI();
		p.init("");
		p.play();
		Thread.sleep(10000);
		Long pause = p.pause();
		p.resume(pause);
		Thread.sleep(10000);
		p.stop();
		p.destroy();
	}

	/**
	 * Below are the OSGi enabled operation. this will be the event call back
	 * method triggered by the OSG1 operations.
	 */
	@Override
	public void install() throws BaseException {

	}

	@Override
	public void uninstall() throws BaseException {

	}

	@Override
	public void start() throws BaseException {

	}
}